﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace Elvex
{
    public static class Math
    {
        /// <summary>
        /// 阶乘
        /// </summary>
        /// <param name="num1"></param>
        /// <param name="num2"></param>
        /// <returns></returns>
        public static long Factorial(int num1, int num2 )
        {
            long res = 1;
            for(int i = num1; i < num2; ++i)
            {
                res *= i;
            }
            return 0;
        }

        public static void Shuffle<T>(this List<T> list)
        {
            if(null == list)
            {
                return;
            }

            if(list.Count <= 1)
            {
                return;
            }

            int currentIndex;
            T tempValue;
            for (int i = list.Count - 1; i >= 0; i--)
            {
                currentIndex = UnityEngine.Random.Range(0, i + 1);
                tempValue = list[currentIndex];
                list[currentIndex] = list[i];
                list[i] = tempValue;
            }
        }

        public static T Random<T>(this List<T> list)
        {
            var r = UnityEngine.Random.Range(0, list.Count);
            return list[r];
        }
    }

    public abstract class Range<T> where T : struct
    {
        public T Max { get; protected set; }

        public T Min { get; protected set; }

        public Range(T min, T max)
        {
            Min = min;
            Max = max;
        }

        public Range(string format, string separator = "_")
        {
            if(string.IsNullOrEmpty(format))
            {
                if (typeof(T) == typeof(int))
                {
                    Min = (T)(object)(int.MinValue);
                    Max = (T)(object)(int.MaxValue);
                }
                else if (typeof(T) == typeof(decimal))
                {
                    Min = (T)(object)(decimal.MinValue);
                    Max = (T)(object)(decimal.MaxValue);
                }
            }
            else
            {
                string[] arr = format.Split(separator.ToCharArray());
                if (arr.Length > 2)
                {
                    throw (new SystemException("Range Construct Error"));
                }

                if (typeof(T) == typeof(int))
                {
                    Min = (T)(object)(int.Parse(arr[0]));
                    Max = arr.Length > 1 ? (T)(object)(int.Parse(arr[1])) : Min;
                }
                else if (typeof(T) == typeof(decimal))
                {
                    Min = (T)(object)(decimal.Parse(arr[0]));
                    Max = arr.Length > 1 ? (T)(object)(decimal.Parse(arr[1])) : Min;
                }
            }
        }

        /// <summary>
        /// ToString()
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Format("{{Min:{0},Max:{1}}}", Min, Max);
        }

        /// <summary>
        /// 返回简短的String描述
        /// </summary>
        /// <returns></returns>
        public string ToBriefString()
        {
            return string.Format("{0}-{1}", Min, Max);
        }
    }

    public interface IRange<T> where T : struct
    {
        bool Contain( T value );
    }

    public class RangeInt : Range<int>, IRange<int>
    {
        public RangeInt(int min, int max) : base(min, max)
        {

        }

        public RangeInt(string format, string separator = "_") : base(format, separator)
        {

        }

        public bool Contain(int value)
        {
            return value >= Min && value <= Max;
        }

        public int Random()
        {
            return UnityEngine.Random.Range(Min, Max);
        }
    }

    public class RangeFloat : Range<float>, IRange<float>
    {
        public RangeFloat(float min, float max) : base(min, max)
        {

        }

        public RangeFloat(string format, string separator = "-") : base(float.MinValue, float.MaxValue)
        {
            if (!string.IsNullOrEmpty(format))
            {
                string[] arr = format.Split(separator.ToCharArray());
                if (arr.Length > 2)
                {
                    throw (new SystemException("Range Construct Error"));
                }

                Min = float.Parse(arr[0]);
                Max = float.Parse(arr[1]);
            }
        }

        public bool Contain(float value)
        {
            return value >= Min && value <= Max;
        }

        public float Random()
        {
            return UnityEngine.Random.Range(Min, Max);
        }
    }

    public class WeightList<T>
    {
        class WeightItem
        {
            public int Weight { get; } = 0;

            public T Item { get; }

            public WeightItem(T item, int weight)
            {
                Weight = weight;
                Item = item;
            }
        }
        List<WeightItem> m_items = new List<WeightItem>();
        int m_weightTotal = 0;

        public int Size { get { return m_items.Count; } }

        public void Add(T item, int weight)
        {
            m_items.Add(new WeightItem(item, weight));
            m_weightTotal += weight;
        }

        public T Random()
        {
            if (m_items.Count < 1)
            {
                return default;
            }
            else if (m_items.Count == 1)
            {
                return m_items[0].Item;
            }
            else
            {
                var r = UnityEngine.Random.Range(0, m_weightTotal);
                int weight = 0;
                foreach (var i in m_items)
                {
                    weight += i.Weight;
                    if (weight > r)
                    {
                        return i.Item;
                    }
                }
                return default;
            }
        }

        public List<T> Random(int num)
        {
            List<T> list = new List<T>();
            if (num > m_items.Count)
            {
                throw new System.Exception("数据错误");
            }
            else if (num == m_items.Count)
            {
                foreach (var item in m_items)
                {
                    list.Add(item.Item);
                }
            }
            else
            {
                int weighttotal = m_weightTotal;
                for (int i = 0; i < num; i++)
                {
                    var r = UnityEngine.Random.Range(0, weighttotal);
                    int weight = 0;
                    foreach (var item in m_items)
                    {
                        if (list.Contains(item.Item))
                        {
                            continue;
                        }

                        weight += item.Weight;
                        if (weight > r)
                        {
                            list.Add(item.Item);
                            weighttotal -= item.Weight;
                            break;
                        }
                    }
                }
            }
            return list;
        }

        public bool TryRandom(int num, out List<T> list)
        {
            list = new List<T>();
            if (num > m_items.Count)
            {
                Debug.LogErrorFormat("WeightList random error.WeightList needs to randomlt pcik {0} but there's only {1}", num, m_items.Count);
                return false;
            }
            else if (num == m_items.Count)
            {
                foreach (var item in m_items)
                {
                    list.Add(item.Item);
                }
            }
            else
            {
                int weighttotal = m_weightTotal;
                for (int i = 0; i < num; i++)
                {
                    var r = UnityEngine.Random.Range(0, weighttotal);
                    int weight = 0;
                    foreach (var item in m_items)
                    {
                        if (list.Contains(item.Item))
                        {
                            continue;
                        }

                        weight += item.Weight;
                        if (weight > r)
                        {
                            list.Add(item.Item);
                            weighttotal -= item.Weight;
                            break;
                        }
                    }
                }
            }
            return true;
        }
    }
}
